package com.lds.erp.gateway.filter;

import java.io.IOException;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import com.dph.common.utils.utils.StringUtil;
import com.lds.erp.common.util.AccountCache;
import com.lds.erp.common.util.AccountUtils;
import com.lds.erp.util.JsonUtils;
import com.lds.erp.util.SpringRequestUtils;

/**
 * @author hongsir
 * @apiNote 用户token认证过滤器
 * servlet3.0 + 的过滤器注入方式,无法设置过滤顺序,只能用命名规则限制
 * 如:Filter+数字+名字 命名,顾名思义数字越小过滤优先级越高
 * @date 2018/5/18 下午3:21
 */
public class FilterToken implements Filter {
    private static final Logger log = LoggerFactory.getLogger(FilterToken.class);
    /**
     * 允许访问的正则
     */
    private Pattern[] allowPattern = null;

    public FilterToken() {
        log.info("========初始化用户认证过滤器========");
    }

    @Override
    public void init(FilterConfig filterConfig) {
        int len = FilterConstants.FILTER_1_ALLOWED_NOT_LOGIN_PATHS.length;
        allowPattern = new Pattern[len];
        for (int i = 0; i < len; i++) {
            // 支持匹配星号
            allowPattern[i] = Pattern.compile(FilterConstants.FILTER_1_ALLOWED_NOT_LOGIN_PATHS[i].trim().replaceAll("\\*", ".*"));
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

        // 获取到请求路径
        String requestUrl = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length()).replaceAll("[/]+$", "");
        boolean allow = allow(requestUrl);
        if (!allow) {
            // 从head中获取token
            String token = httpRequest.getHeader("token");
            // 从url中获取token
            if (StringUtil.isBlank(token)) {
                token = httpRequest.getParameter("token");
                log.info("请求:{} 开发者模式 url取值:{}", requestUrl, token);
            }
            if (StringUtil.isBlank(token)) {
                responseError(httpRequest, httpResponse, "会话凭证[token]不能为空!");
                log.error("请求:{} 被拦截 错误信息:{}", requestUrl, "会话凭证[token]不能为空!");
                return;
            }

            // 获取请求来源
            String app = httpRequest.getHeader("app");
            if (StringUtil.isBlank(app)) {
                responseError(httpRequest, httpResponse, "移动端必须传入[app]标识!");
                log.error("请求:{} 被拦截 错误信息:{}", requestUrl, "移动端[app]标识未传!");
                return;
            }

            // 根据token获取缓存信息
            AccountCache accountCache = AccountUtils.getAccount(token);

            // 未登录提示
            if (ObjectUtils.isEmpty(accountCache)) {
                responseError(httpRequest, httpResponse, "请登陆后再试!");
                log.error("请求:{} 被拦截 错误信息:{}", requestUrl, "未找到账号登录信息!");
                return;

            }

            // 单用户登录提示
            if (!token.equals(accountCache.getMchToken())) {
                responseError(httpRequest, httpResponse, "该账号已在其他地方登录,被迫下线,如非本人操作,请修改密码!");
                log.error("请求:{} 被拦截,错误信息:{}", requestUrl, "该账号已在其他地方登录,被迫下线!");
                return;
            }

            // 重置账号缓存时间
            AccountUtils.cacheAccount(accountCache);
            // 设置账号线程资源
            AccountUtils.setAccount(accountCache);
        }

        // 执行下一个过滤器
        filterChain.doFilter(httpRequest, httpResponse);
    }

    @Override
    public void destroy() {

    }

    /**
     * 正则比对url
     *
     * @param requestUrl
     * @return
     */
    private boolean allow(String requestUrl) {
        log.info("2当前认证接口:{},接口集:{}", requestUrl, allowPattern);
        boolean flag = false;
        for (Pattern pat : allowPattern) {
            if (pat.matcher(requestUrl).matches()) {
                flag = true;
                break;
            }
        }
        return flag;
    }

    /**
     * 返回错误信息
     *
     * @param httpRequest
     * @param httpResponse
     * @param msg
     * @throws IOException
     */
    private void responseError(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String msg) throws IOException {
        httpResponse.setCharacterEncoding("UTF-8");
        SpringRequestUtils.responseJson(httpResponse, JsonUtils.fail("401", msg, null));
    }

}
